﻿// -----------------------------------------------------------------------------------------------
//  <copyright file="TestProjectExporter.cs" company="http://buginbox.wordpress.com/">
//    Copyright (c) GNU General Public License version 2 (GPLv2)
//  </copyright>
// -----------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using Microsoft.TeamFoundation.TestManagement.Client;

using TestPlanMigration.Tree;

namespace TestPlanMigration
{
    /// <summary>
    /// The test project migrator.
    /// </summary>
    public class TestProjectExporter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TestProjectExporter"/> class. Prevents a default instance of the <see cref="TestProjectMigrator"/> class from being created.
        /// </summary>
        /// <param name="sourceTestProject">The source test project.</param>
        /// <param name="rootItemToExport">The test plan to export.</param>
        /// <param name="exportToFileName">Name of the export to file.</param>
        /// <param name="logger">The logger.</param>
        public TestProjectExporter(TfsProjectTest sourceTestProject, TfsTestItem rootItemToExport, string exportToFileName, StreamWriter logger)
        {
            _sourceTestProject = sourceTestProject;
            _rootItemToExport = rootItemToExport;
            _exportToFileName = exportToFileName;
            _logger = logger;
        }

        /// <summary>
        /// Gets or sets the source test project.
        /// </summary>
        /// <value>
        /// The source test project. 
        /// </value>
        private TfsProjectTest _sourceTestProject { get; set; }

        /// <summary>
        /// Gets or sets the destination test project.
        /// </summary>
        /// <value>
        /// The destination test project. 
        /// </value>
        private TfsTestItem _rootItemToExport { get; set; }

        /// <summary>
        /// Gets or sets the name of the _export to file.
        /// </summary>
        /// <value>
        /// The name of the _export to file.
        /// </value>
        private string _exportToFileName { get; set; }

        /// <summary>
        /// Gets or sets the _logger.
        /// </summary>
        /// <value>
        /// The _logger. 
        /// </value>
        private StreamWriter _logger { get; set; }

        /// <summary>
        /// Writes the specified message.
        /// </summary>
        /// <param name="message">
        /// The message. 
        /// </param>
        private void Write(string message)
        {
            if (_logger == null)
            {
                return;
            }

            _logger.WriteLine(message);
        }

        /// <summary>
        /// Exports the test plans.
        /// </summary>
        public void ExportTestPlan()
        {
            if (_rootItemToExport == null || _rootItemToExport.Type != TfsTestItemType.TestPlan)
            {
                throw new ArgumentException("Invalid Root Item Type. Must be a test plan.");
            }

            var plans = _sourceTestProject.AllPlans;

            if (plans == null)
            {
                Write("Source project has no plan to export.");
                return;
            }

            var plan = plans.FirstOrDefault(x => x.Id == _rootItemToExport.Id);

            if (plan == null)
            {
                Write("Source project has no plan to export for ID = " + _rootItemToExport.Id);
                return;
            }

            if (plan.RootSuite != null && plan.RootSuite.Entries.Count > 0)
            {
                string path = string.Empty;
                
                using (var exportFile = new StreamWriter(_exportToFileName, false, Encoding.GetEncoding(1252)))
                {
                    AppendHeaderRow(exportFile);
                    ExportTestSuites(plan.RootSuite, path, exportFile);
                }
            }
        }

        /// <summary>
        /// Appends to path.
        /// </summary>
        /// <param name="currentPath">The current path.</param>
        /// <param name="pathToAppend">The path to append.</param>
        private void AppendToPath(ref string currentPath, string pathToAppend)
        {
            currentPath += " > " + pathToAppend;
        }

        /// <summary>
        /// Exports the test suites.
        /// </summary>
        /// <param name="parentSuite">The parent suite.</param>
        /// <param name="path">The path.</param>
        /// <param name="exportFile">The export file.</param>
        private void ExportTestSuites(IStaticTestSuite parentSuite, string path, StreamWriter exportFile)
        {
            if (parentSuite == null)
            {
                throw new ArgumentNullException("parentSuite");
            }

            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            if (exportFile == null)
            {
                throw new ArgumentNullException("exportFile");
            }

            AppendToPath(ref path, parentSuite.Title);

            foreach (ITestSuiteEntry suiteEntry in parentSuite.Entries)
            {
                var suite = suiteEntry.TestSuite as IStaticTestSuite;

                if (suite != null)
                {
                    ExportTestCases(suite, path, exportFile);

                    if (suite.Entries.Count > 0)
                    {
                        ExportTestSuites(suite, path, exportFile);
                    }
                }
            }
        }

        /// <summary>
        /// Exports the test cases.
        /// </summary>
        /// <param name="parentSuite">The parent suite.</param>
        /// <param name="path">The path.</param>
        /// <param name="exportFile">The export file.</param>
        private void ExportTestCases(IStaticTestSuite parentSuite, string path, StreamWriter exportFile)
        {
            if (parentSuite == null)
            {
                throw new ArgumentNullException("parentSuite");
            }

            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            if (exportFile == null)
            {
                throw new ArgumentNullException("exportFile");
            }

            AppendToPath(ref path, parentSuite.Title);

            // Will bring only the Test Case under a specific Test Suite.
            ITestSuiteEntryCollection testCases = parentSuite.TestCases;

            foreach (ITestSuiteEntry testCase in testCases)
            {
                AppendTestCaseRow(testCase.TestCase, path, exportFile);
            }
        }

        /// <summary>
        /// Column Separator
        /// </summary>
        private const string _columnSeparator = ";";

        /// <summary>
        /// Row Separator
        /// </summary>
        private const string _rowSeparator = "\r\n";

        /// <summary>
        /// Appends the test case row.
        /// </summary>
        /// <param name="exportFile">The export file.</param>
        private void AppendHeaderRow(StreamWriter exportFile)
        {
            var row = new StringBuilder();

            ////AppendColumn(row, "Order", true);
            AppendColumn(row, "Id", true);
            AppendColumn(row, "Title", true);
            ////AppendColumn(row, "Configuration", true);
            AppendLastColumn(row, "Path", true);

            exportFile.Write(row.ToString());
        }

        /// <summary>
        /// Appends the test case row.
        /// </summary>
        /// <param name="testCase">The test case.</param>
        /// <param name="path">The path.</param>
        /// <param name="exportFile">The export file.</param>
        private void AppendTestCaseRow(ITestCase testCase, string path, StreamWriter exportFile)
        {
            var row = new StringBuilder();

            ////AppendColumn(row, string.Empty);
            AppendColumn(row, testCase.Id);
            AppendColumn(row, testCase.Title, true);
            ////AppendColumn(row, string.Empty, true);
            AppendLastColumn(row, path, true);

            exportFile.Write(row.ToString());
        }

        /// <summary>
        /// Appends the specified row.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="value">The value.</param>
        /// <param name="includeInQuotes">if set to <c>true</c> [include in quotes].</param>
        /// <param name="separator">The separator.</param>
        private static void Append(StringBuilder row, object value, bool includeInQuotes, string separator)
        {
            if (includeInQuotes)
            {
                row.Append("\"");
            }

            row.Append(value.ToString());

            if (includeInQuotes)
            {
                row.Append("\"");
            }

            row.Append(separator);
        }

        /// <summary>
        /// Appends the column.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="value">The value.</param>
        /// <param name="includeInQuotes">if set to <c>true</c> [include in quotes].</param>
        private static void AppendColumn(StringBuilder row, object value, bool includeInQuotes = false)
        {
            Append(row, value, includeInQuotes, _columnSeparator);
        }

        /// <summary>
        /// Appends the last column.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="value">The value.</param>
        /// <param name="includeInQuotes">if set to <c>true</c> [include in quotes].</param>
        private void AppendLastColumn(StringBuilder row, object value, bool includeInQuotes = false)
        {
            Append(row, value, includeInQuotes, _rowSeparator);
        }
    }
}